<?php
// +----------------------------------------------------------------------
// | INPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2021 https://inphp.cc All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/MIT )
// +----------------------------------------------------------------------
// | Author: lulanyin <me@lanyin.lu>
// +----------------------------------------------------------------------
namespace Inphp\Service\Websocket;

use Inphp\Service\Context;
use Inphp\Service\Object\Message;
use Inphp\Service\Router;
use Inphp\Service\Service;

class Server extends \Inphp\Service\Server
{
    /**
     * 服务类型
     * @var string
     */
    public string $server_type = Service::WS;

    /**
     * 初始化
     * Server constructor.
     * @param bool $swoole
     */
    public function __construct(bool $swoole = true)
    {
        //执行默认
        parent::__construct(true);
        //websocket特有
        $this->server->on('open', [$this, 'onOpen']);
        $this->server->on('close', [$this, 'onClose']);
        $this->server->on('message', [$this, 'onMessage']);
    }

    /**
     * 客户端握手成功后，会触发这个，如果私自设置了握手回调，则不会触发这个事件
     * @param \Swoole\WebSocket\Server $server
     * @param \Swoole\Http\Request $request
     */
    public function onOpen(\Swoole\WebSocket\Server $server, \Swoole\Http\Request $request){
        //保存客户端ID到当前协程
        \Inphp\Service\Context::setClientId($request->fd);
        //主域名
        $host = $request->header['host'];
        $ip = $swoole_request->header['x-real-ip'] ?? ($request->server['remote_addr'] ?? null);
        //路径信息
        $path = $request->server['path_info'] ?? '';
        $uri = $request->server['request_uri'] ?? '';
        $path = !empty($path) ? $path : $uri;
        //交给路由处理，由HTTP控制器处理数据返回，得到数据
        $client = new \Inphp\Service\Object\Client([
            "host"      => $host,
            "ip"        => $ip,
            "method"    => 'upgrade',
            "cookie"    => $request->cookie,
            "get"       => $request->get,
            "post"      => $request->post,
            "files"     => $request->files,
            "raw_post_data" => $request->rawContent(),
            "uri"       => $path,
            "origin"    => $request->header['origin'] ?? '',
            "id"        => $request->fd
        ]);
        //
        \Inphp\Service\Context::setClient($client, $request->fd);
        //中间键
        $this->processMiddlewares("on_open", [$server, $request]);
    }

    /**
     * 客户端连接已关闭
     * @param \Swoole\WebSocket\Server $server
     * @param int $fd
     * @param int $reactor_id
     */
    public function onClose(\Swoole\WebSocket\Server $server, int $fd, int $reactor_id){
        //保存client_id到当前协程上下文
        \Inphp\Service\Context::setClientId($fd);
        //中间键
        $this->processMiddlewares("on_close", [$server, $fd, $reactor_id]);
        //Context上下文移除客户端
        \Inphp\Service\Context::removeClient($fd);
        \Inphp\Service\Context::clean($fd);
    }

    /**
     * 接收到客户端消息
     * @param \Swoole\WebSocket\Server $server
     * @param \Swoole\WebSocket\Frame $frame
     */
    public function onMessage(\Swoole\WebSocket\Server $server, \Swoole\WebSocket\Frame $frame){
        //保存client_id到当前协程上下文
        \Inphp\Service\Context::setClientId($frame->fd);
        //frame 也保存进去
        \Inphp\Service\Context::setWebsocketFrame($frame);
        //中间键
        $this->processMiddlewares('on_message', [$server, $frame]);
        //仅允许接收JSON数据格式
        $json = !empty($frame->data) ? @json_decode($frame->data, true) : [];
        $json = is_array($json) ? $json : ["data" => $frame->data];
        $uri = $json['event'] ?? ($json['uri'] ?? '/');
        $status = Router::process($uri, 'upgrade', $this->server_type);
        //保存路由状态到协程上下文
        Context::setRouterStatus($status);
        $message = new \Inphp\Service\Object\Message($json);
        //将消息保存到协程上下文
        Context::setMessage($message);
        //得到状太，执行控制器
        if($status->status == 200){
            if(!empty($status->controller) && class_exists($status->controller)){
                //仅处理能找到控制器的，其它数据一致不处理
                $controller = new $status->controller();
                //中间键
                $this->processMiddlewares('before_execute', [$controller, $status->method, $server, $frame]);
                if(method_exists($controller, $status->method)){
                    //执行方法
                    $result = call_user_func_array([$controller, $status->method], [$message, $server, $frame]);
                    if($result instanceof Message)
                    {
                        //执行前处理中间键
                        $this->processMiddlewares('before_push', [$result, $controller, $status->method, $server, $frame]);
                        //返回数据是Message，则将数据通知到客户端
                        $server->push($frame->fd, $result->toJson());
                    }
                    return;
                }
            }
        }
        //未知数据
        $this->processMiddlewares('unknow_data', [$server, $frame]);
    }

    /**
     * 服务启动
     */
    public function start(){
        //启动前
        $this->beforeStart();
        //启动服务
        $this->server->start();
    }
}